import java.util.*;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

public class Bank {
    private final Account[] accounts;
    private final BlockingQueue<Transaction> transactionQueue;
    private final List<Thread> workerThreads;
    private final int numWorkers;

    public Bank(int numAccounts, int initialBalance, int numWorkers) {
        this.accounts = new Account[numAccounts];
        for (int i = 0; i < numAccounts; i++) {
            accounts[i] = new Account(i + 1, initialBalance);
        }
        this.transactionQueue = new ArrayBlockingQueue<>(100);
        this.numWorkers = numWorkers;
        this.workerThreads = new ArrayList<>();
    }

    public void startWorkers() {
        for (int i = 0; i < numWorkers; i++) {
            Thread worker = new Thread(() -> {
                try {
                    while (true) {
                        Transaction transaction = transactionQueue.take();
                        if (transaction.isPoisonPill()) {
                            // 重新放入毒丸，让其他线程也能结束
                            transactionQueue.put(transaction);
                            break;
                        }

                        Account from = accounts[transaction.getFromAccount() - 1];
                        Account to = accounts[transaction.getToAccount() - 1];
                        from.transferTo(to, transaction.getAmount());
                    }
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            });
            worker.start();
            workerThreads.add(worker);
        }
    }

    public void addTransaction(Transaction transaction) throws InterruptedException {
        transactionQueue.put(transaction);
    }

    public void waitForCompletion() throws InterruptedException {
        for (Thread worker : workerThreads) {
            worker.join();
        }
    }

    public void printAccountBalances() {
        Arrays.stream(accounts)
                .sorted(Comparator.comparingInt(Account::getId))
                .forEach(account -> System.out.printf("acct:%02d  bal:%4d  trans:%d%n",
                        account.getId(), account.getBalance(), account.getTransactionNum()));
    }
}